package com.syf.core.config;

import cn.hutool.core.bean.BeanDesc;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.PropDesc;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.env.PropertiesPropertySource;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * BeanPostProcessor扩展点。
 * 将@Value @Configuration 定义的配置参数设置回Environment内，这样配置参数中就包括Configuration中默认定义的参数.
 */
public class SpringConfigBeanArgumentLoader implements BeanPostProcessor {
    private final SyApplicationConfiguration configuration;

    private final Map<String, SpringFieldValue> syApplicationConfigSpringValues = new HashMap<>();

    public SpringConfigBeanArgumentLoader(SyApplicationConfiguration configuration) {
        this.configuration = configuration;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> beanClass = bean.getClass();
        if (beanClass.getName().contains("$$EnhancerBySpringCGLIB")) { //com.framework.core.config.properties.DataSourcesConfig$$EnhancerBySpringCGLIB$$2ce4b8f3
            beanClass = bean.getClass().getSuperclass();
        }

        ConfigurationProperties configurationPropertiesAnno = beanClass.getAnnotation(ConfigurationProperties.class);
        if (configurationPropertiesAnno != null) {
            String prefix = configurationPropertiesAnno.prefix();
            Map<String, SpringFieldValue> springFieldValueMap = new HashMap<>();
            parseConfigurationProperties(prefix, "", springFieldValueMap, bean, bean);
            syApplicationConfigSpringValues.putAll(springFieldValueMap);

            PropertiesPropertySource propertiesPropertySource = buildPropertiesPropertySource(beanName, springFieldValueMap);
            configuration.loadSpringConfigurationBeanArgs(propertiesPropertySource);
        }

        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    /**
     * 构建PropertiesPropertySource
     */
    private PropertiesPropertySource buildPropertiesPropertySource(String sourceName, Map<String, SpringFieldValue> springFieldValueMap) {
        Properties properties = new Properties();
        for (String s : springFieldValueMap.keySet()) {
            SpringFieldValue springFieldValue = springFieldValueMap.get(s);
            // 只加载在Spring中显式配置参数未定义，但在@ConfigurationProperties存在的配置参数的初始默认值
            if (!configuration.containsProperty(springFieldValue.getConfigKey())) {
                // warning:springFieldValue.getInitialValue()的值一定是基础类型
                String initialValue = ObjectUtil.toString(springFieldValue.getInitialValue());
                properties.setProperty(springFieldValue.getConfigKey(), initialValue);
            }
        }

        if (properties.size() == 0) {
            return null;
        }

        return new PropertiesPropertySource(sourceName, properties);
    }

    /***
     * 解析带有@ConfigurationProperties标签的spring bean对应的配置参数信息
     * */
    private void parseConfigurationProperties(String basePrefix, String beanPathPrefix, Map<String, SpringFieldValue> springFieldValueMap, Object bean, Object springBean) {
        Class<?> beanClass = bean.getClass();
//        if(beanClass.getPackage().getName().startsWith("com.frame")){}
        if (beanClass.getName().contains("$$EnhancerBySpringCGLIB")) { //com.framework.core.config.properties.DataSourcesConfig$$EnhancerBySpringCGLIB$$2ce4b8f3
            beanClass = bean.getClass().getSuperclass();
        }
        BeanDesc beanDesc = BeanUtil.getBeanDesc(bean.getClass());

        Field[] declaredFields = beanClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            String fieldName = declaredField.getName();
            PropDesc propDesc = beanDesc.getProp(fieldName);
            if (propDesc == null) {
                continue;
            }
            if (ClassUtil.isBasicType(propDesc.getFieldClass()) || propDesc.getFieldClass().equals(String.class)) {
                // 如果为基础类型，则为一个SpringFieldValue
                String beanPath = fieldName;
                if (!StrUtil.isEmptyIfStr(beanPathPrefix)) {
                    beanPath = beanPathPrefix + "." + beanPath;
                }
                String configKey = basePrefix + "." + beanPath;
                SpringFieldValue springFieldValue = new SpringFieldValue(configKey, propDesc.getValue(bean), springBean, beanPath);
                springFieldValueMap.put(springFieldValue.getConfigKey(), springFieldValue);
            } else {
                // 如果非基础类型成员变量，则为嵌套式参数
                if (propDesc.getFieldClass().isMemberClass() && propDesc.getValue(bean) != null) {
                    parseConfigurationProperties(basePrefix, fieldName, springFieldValueMap, propDesc.getValue(bean), springBean);
                }
            }
        }
    }
}
